/*
 * clock.c
 *
 * Copyright 2011 Pieter Agten
 *
 * This file is part of Yarf.
 *
 * Yarf is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Yarf is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Yarf.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   clock.c
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   30 okt 2011
 * @brief  Real-time clock for counting the uptime and for providing a time base 
 *         for periodic tasks.
 */


#include "clock.h"

#include "yarf.h"
#include "fastio.h"
#include "scheduling/periodic.h"
#include "scheduling/periodic_notify.h"
#include "movement/advance.h" //TODO: decrease coupling between clock.c and advance
#include "util/math.h"

#include <avr/interrupt.h>
#include <util/atomic.h>

/**
 * The number of ticks the hardware timer will take in one millisecond.
 */
#define TIMER_TICKS_PER_MS (F_CPU/8/1000)


/**
 * The number of milliseconds passed since the clock was initialized.
 */
static volatile unsigned long millis;

/**
 * Initializes the hardware timer.
 */
static void
timer1_init(void)
{
  /* Disable timer 1 */
  TCCR1B = 0;

  /* Clear all timer 1 interrupt flags */
  TIFR1 = _BV(ICF1)|_BV(OCF1B)|_BV(OCF1A)|_BV(TOV1);

  /* Enable the timer 1 OCR1A interrupt */
  TIMSK1 = _BV(OCIE1B);

#if ADVANCE_ALGORITHM
  TIMSK1 |= _BV(OCIE1A); //TODO: decrease coupling between clock.c and advance
#endif

  /* 16-bit register writes using TEMP must be performed atomically */
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    /* Clear the timer 1 value */
    TCNT1 = 0;
    /* Set the timer 1 B top value */
    OCR1B = TIMER_TICKS_PER_MS;

#if ADVANCE_ALGORITHM
    /* Set the timer 1 A top value */
    OCR1A = EXTRUDER_TIMER_TICKS; //TODO: decrease coupling between clock.c and advance
#endif
  }

  /* Enable timer 1 with /8 prescaler in normal mode */
  TCCR1A = 0;
  TCCR1B = (2 << CS10);
}


/**
 * Interrupt service routine which is executed every millisecond.
 *
 * This routine increments \a millis by one and notifies the periodic scheduler
 * that a millisecond has passed.
 */
ISR(TIMER1_COMPB_vect, ISR_NOBLOCK)
{
  /* 16-bit register writes using TEMP must be performed atomically */
  ATOMIC_BLOCK(ATOMIC_FORCEON) {
    /* Set the timer 1 B top value */
    OCR1B += TIMER_TICKS_PER_MS;
  }

  millis += 1;
  periodic_notify_ms();  
}

void
clock_init(void)
{
  millis = 0;
  periodic_init();
  timer1_init();
}

unsigned long
clock_millis(void)
{
  return millis;
}


